using System;

namespace Param.TraceListener
{
    public class TcpTraceListener : System.Diagnostics.TraceListener
    {
        private static System.Collections.Generic.Queue<string> m_objMessageSink = new System.Collections.Generic.Queue<string>();

        private static System.Threading.Thread m_tMessagePump = null;

        private System.Collections.Generic.List<System.Net.Sockets.TcpClient> m_objClients = new System.Collections.Generic.List<System.Net.Sockets.TcpClient>();

        private System.Net.Sockets.TcpListener m_objTcpListener = null;

        private System.Threading.Thread m_tListener = null;

        private int m_nBackLog = 5;

        private bool m_blnIsMessagePumpActive = false;

        private bool m_blnIsClientListenerActive = false;

        public TcpTraceListener(int nPort)
        {
            m_objTcpListener = new System.Net.Sockets.TcpListener(System.Net.IPAddress.Any, nPort);
            m_tListener = new System.Threading.Thread(new System.Threading.ThreadStart(ClientListener));
            m_blnIsClientListenerActive = true;
            m_tListener.Start();

            if (m_tMessagePump == null)
            {
                m_tMessagePump = new System.Threading.Thread(new System.Threading.ThreadStart(MessagePump));
                m_blnIsMessagePumpActive = true;
                m_tMessagePump.Start();
            }
        }

        ~TcpTraceListener()
        {
            WriteLine("Terminating log publisher...");

            m_blnIsMessagePumpActive = false;
            if (m_tMessagePump != null)
            {
                m_tMessagePump.Join(2000);
                if (m_tMessagePump.ThreadState.Equals(System.Threading.ThreadState.Running))
                    m_tMessagePump.Abort();
            }

            m_blnIsClientListenerActive = false;
            if (m_tListener != null)
            {                
                m_tListener.Join(2000);
                if (m_tListener.ThreadState.Equals(System.Threading.ThreadState.Running))
                    m_tListener.Abort();
            }
        }

        private void MessagePump()
        {
            while (m_blnIsMessagePumpActive)
            {
                try
                {
                    if (m_objMessageSink.Count > 0)
                        PublishMessage(m_objMessageSink.Dequeue());
                }
                catch (System.InvalidOperationException) { }

                System.Threading.Thread.Sleep(1);
            }

            // flush any remaining log messages
            try
            {
                while (m_objMessageSink.Count > 0)
                    PublishMessage(m_objMessageSink.Dequeue());
            }
            catch (System.InvalidOperationException) { }
        }

        private void PublishMessage(string strMessage)
        {
            try
            {
                System.Net.Sockets.NetworkStream objNetworkStream = null;
                byte[] buffer = null;

                foreach (System.Net.Sockets.TcpClient objTcpClient in m_objClients.ToArray())
                {
                    if (!objTcpClient.Connected)
                    {
                        m_objClients.Remove(objTcpClient);
                        continue;
                    }

                    objNetworkStream = objTcpClient.GetStream();
                    if (objNetworkStream == null)
                        continue;

                    buffer = System.Text.ASCIIEncoding.ASCII.GetBytes(strMessage);
                    if (buffer == null)
                        continue;

                    objNetworkStream.Write(buffer, 0, buffer.Length);
                }
            }
            catch { }
        }

        private void ClientListener()
        {
            m_objTcpListener.Start(m_nBackLog);

            System.Net.Sockets.TcpClient objTcpClient = null;
            while (m_blnIsClientListenerActive)
            {
                objTcpClient = m_objTcpListener.AcceptTcpClient();
                
                if (objTcpClient != null)
                    m_objClients.Add(objTcpClient);
            }
        }

        public override void Write(string message)
        {
            m_objMessageSink.Enqueue(message);
        }

        public override void WriteLine(string message)
        {
            message += Environment.NewLine;
            Write(message);
        }
    }
}